import { Injectable } from '@nestjs/common';
import { DbService } from '../../db/db.service';
import { CacheService } from '@app/cache';
import WorkException from 'exception/work.exception';
import { Device } from '@app/db/entity/Device';
import { DeviceType } from '@app/db/entity/DeviceType';
import { FindOptionsWhere, In, LessThan } from 'typeorm';
import { DEVICE_TYPE } from 'constant/DEVICE_TYPE';
import { AlicloudService } from '@app/alicloud';
import { ConfigureService } from '@app/configure';
import { DEVICE_CTR_RESULT } from 'constant/DEVICE_CTR_RESULT';
import { DEVICE_CTR_ORDER } from 'constant/DEVICE_CTR_ORDER';
import { CleanRecord } from '@app/db/entity/CleanRecord';


@Injectable()
export class DeviceService {
	private aliToDeviceTopicReg:string;

	constructor(
		private readonly db:DbService,
		private readonly cache:CacheService,
		private readonly alicloud:AlicloudService,
		private readonly config:ConfigureService,
	){
		this.aliToDeviceTopicReg = config.get('ALI_TO_DEVICE_TOPIC_REG')
	}


	async deviceOfUser(userId:number) {
		const deviceList:Array<Device & {deviceType? : DeviceType}> = await this.db.device.find({userId}, {id : true, deviceTypeId : true, connectStatus : true})

		const deviceTypeIds:number[] = []

		deviceList.forEach(item => {
			if(!deviceTypeIds.includes(item.deviceTypeId)) {
				deviceTypeIds.push(item.deviceTypeId)
			}
		})

		const deviceTypes = await this.db.deviceType.find({id : In(deviceTypeIds)}, {id : true, name : true, code : true, icon: true})

		deviceList.forEach(item => {
			const deviceType = deviceTypes.find(t => t.id == item.deviceTypeId)
			if(deviceType) {
				item.deviceType = deviceType
			}
		})


		return deviceList
	}

	async bindDevice(deviceId:number, userId:number) {
		const device = await this.db.device.findOne({id : deviceId}, {userId : true, id : true, deviceTypeId : true})
		if(!device) return false
		if(device.userId != 0) throw new WorkException('设备已被绑定')
	
		let lockKey = 'binddevice.' + deviceId

		const lock = await this.cache.set(lockKey, '', 10, 'NX')
		if(!lock) return false

        const deviceType = await this.db.deviceType.findOne({id : device.deviceTypeId}, {code : true})
        
        if(deviceType.code == DEVICE_TYPE.water) { //添加水机的，只允许添加一台水机
            const r = await this.db.device.isExistsByWhere({userId, deviceTypeId : device.deviceTypeId})
            if(r) throw new WorkException('只能有一台水机')
        }

		const r = await this.db.device.update({id : deviceId}, {userId : userId})
		
		this.cache.del(lockKey)

		return r > 0
		

	}

	async unbindDevice(deviceId:number, userId:number) {
		const device = await this.db.device.findOne({id : deviceId}, {userId : true, id : true})
		if(!device) return false
		if(device.userId != userId) return true
		
		
		const r = await this.db.device.update({id : deviceId}, {userId : 0})
		

		return r > 0
	
	}


	//启动停止设备
	async startDevice(deviceId:number, userId:number, param:'0'|'1') {
		const device = await this.db.device.findOne({id : deviceId}, {userId : true, id : true, deviceTypeId : true})
		if(!device || device.userId != userId) return false
		
		const deviceType = await this.db.deviceType.findOne({id : device.deviceTypeId}, {aliProduct : true})
        if(!deviceType) return false

		
		

		const r = await this.pub(deviceId, deviceType.aliProduct, {"v":"ss_1","goal":"1-1","port":"1-1","childPort":"","cmd":DEVICE_CTR_ORDER.SET_START_DEVICE,"param":param})
		if(r.success) {
			this.cache.set(DEVICE_CTR_ORDER.SET_START_DEVICE + ':' + deviceId, DEVICE_CTR_RESULT.WAIT, 10)

			return true
		}

		return false

	}

	async startDeviceRes(deviceId:number|string) {
		return this.cache.get(DEVICE_CTR_ORDER.SET_START_DEVICE + ':' + deviceId)
	}

	async startClean(deviceId:number, userId:number, param:'0'|'1') {
		const device = await this.db.device.findOne({id : deviceId}, {userId : true, id : true, deviceTypeId : true})
		if(!device || device.userId != userId) return false
		
		const deviceType = await this.db.deviceType.findOne({id : device.deviceTypeId}, {aliProduct : true})
        if(!deviceType) return false

		let id = 0
		const msg:{[key:string]:any} = {"v":"ss_1","goal":"1-1","port":"1-1","childPort":"","cmd":DEVICE_CTR_ORDER.SET_CLEAN_DEVICE,"param":param}

		if(param == '1') {
			const r = await this.db.cleanRecord.insert({
				userId,
				deviceId,
				status : DEVICE_CTR_RESULT.WAIT,
				createTime : Math.floor(Date.now() / 1000)
			})

			if(r != null && r.affectedRows > 0) {
				id = r.rows[0][this.db.cleanRecord.primaryKey]
				msg.msgId = String(id)
			}
		}


		const r = await this.pub(deviceId, deviceType.aliProduct, msg)
		if(r.success) {

			this.cache.set(DEVICE_CTR_ORDER.SET_CLEAN_DEVICE + ':' + deviceId, DEVICE_CTR_RESULT.WAIT, 10)

			return true
		}

		if(param == '1') {
			this.db.cleanRecord.update({id : id}, {status : DEVICE_CTR_RESULT.FAIL})
		}

		return false

	}

	async cleanRes(deviceId:number|string) {
		return this.cache.get(DEVICE_CTR_ORDER.SET_CLEAN_DEVICE + ':' + deviceId)
	}


	async cleanRecordPage(query:Pagination, userId:number) {
		let where:FindOptionsWhere<CleanRecord> = { userId }
		const lastId = query.lastKey || 0
		const pageSize = query.pageSize || 20

		if(lastId > 0) {
			where.id = LessThan(lastId)
		}

		let rows:CleanRecord[] = await this.db.cleanRecord.getRepository().find({
			where,
			skip : 0, 
			take : pageSize, 
			order : {id : 'DESC'}
		})

		return rows
	}

	async setMaxWaterFlow(deviceId:number, userId:number) {
		const device = await this.db.device.findOne({id : deviceId}, {userId : true, id : true, deviceTypeId : true})
		if(!device || device.userId != userId) return false
		
		const deviceType = await this.db.deviceType.findOne({id : device.deviceTypeId}, {aliProduct : true})
        if(!deviceType) return false

		const user = await this.db.user.findOne({id : userId}, {amount : true, rewardAmount : true})

		const flow = Math.floor((user.amount + user.rewardAmount) / 0.1 * 1000)

		const msg = {
			"v":"ss_1",
			"cmd": DEVICE_CTR_ORDER.SET_MAX_WATER_FLOW,
			"goal":"1-1",
			"port":"1-1",
			"param": String(flow),
			"childPort":"",
		}

		const r = await this.pub(deviceId, deviceType.aliProduct, msg)


		if(r.success) {

			this.cache.set(DEVICE_CTR_ORDER.SET_MAX_WATER_FLOW + ':' + deviceId, DEVICE_CTR_RESULT.WAIT, 10)

			return true
		}

		return false
	}

	async setMaxFlowRes(deviceId:number|string) {
		return this.cache.get(DEVICE_CTR_ORDER.SET_MAX_WATER_FLOW + ':' + deviceId)
	}


	public async pub(deviceId:number, productKey:string, msg:{[key:string]:any}) {
		const deviceName = String(deviceId)
		msg.dCore = deviceName



		return this.alicloud.pub(productKey, this.aliToDeviceTopicReg.replace(/\?/, productKey).replace(/\?/, deviceName), deviceName, msg)
	}
}
